﻿
#region Using Statements
using System;
using System.Collections.Generic;//using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Net;
using System.IO;
using Microsoft.Xna.Framework.GamerServices;
#endregion


namespace Pauliver
{
    public class NetworkLobby : BaseMenu
    {
        TerrainSaver ts;
        Texture2D MiniMap;
        Texture2D Minimap_overlay;
        bool Hosting;
        bool HasNewTS;

        SpriteFont font;
        string PlayerCount;
        int pc = 1;
        int tempcounter = 0;

        public static short REQUESTTERRAIN = 0x60;
        public static short TERRAINSAVER = 0x61;
        public static short CreatePlayers = 0x62;
        public static short SETUPPlayer = 0x63;
        public static short SETUPGame = 0x64;
        public static short SETUPFakeGamepad = 0x65;

        public void StartGame()
        {
            if (NetworkManager.Instance.IsHost())
            {
                //this seems kinda silly but were going to do it anyway
                NetworkManager.Instance.RemoveAllCallbacks();
                Game1.Instance.LoadNetworkGame(teams, ts, NetworkManager.Instance);
            }
        }

        public NetworkLobby(TerrainSaver TS, bool IsHost)
            : base()
        {
            NetworkManager.Instance.AddNetworkSessionEndedCallback(Back);
            NetworkManager.Instance.AddHostChangedCallback(HostChanged);
            NetworkManager.Instance.AddGameStartCallback(StartGame);
            HasNewTS = false;
            Hosting = IsHost;



            PlayerCount = "Players: " + 0;
            font = PContentManager.Instance.GetObject<SpriteFont>(@"Content\Art/Fonts/INVADER");

            // Load the MiniMap texture
            MiniMap = PContentManager.Instance.GetObject<Texture2D>("MiniMap");
            Minimap_overlay = PContentManager.Instance.GetObject<Texture2D>(@"Content\Art/GUI/Minimap_overlay");
            // Generate Terrain
            //create the menu
            _menu = new UIBaseMenu(@"Content\Art/GUI/MenuBackground", Vector2.Zero);


            if (Hosting)
            {
                ts = TS;
                GenerateTerrain();
                NetworkManager.Instance.AddPlayerJoinedCallback(WriteTerrain);
                NetworkManager.Instance.AddPCallback(REQUESTTERRAIN, RequestTerrain);
            }
            else
            {
                NetworkManager.Instance.AddNCallBack(TERRAINSAVER, ReadTerrain);
                NetworkManager.Instance.AddNCallBack(CreatePlayers, CreatePlayersCallback);
                NetworkManager.Instance.AddNCallBack(SETUPPlayer, SetupPlayerCallback);
                NetworkManager.Instance.AddNCallBack(SETUPGame, SetupGameCallback);
                NetworkManager.Instance.AddNCallBack(SETUPFakeGamepad, SetupFakeGamepad);
            }

            //Now create buttons
            if (Hosting)
            {
                UIButton b = new UIButton(@"Content\Art/GUI/Start", new Vector2(20, 175));
                _menu.Add(Start, b);
            }
            {
                UIButton b = new UIButton(@"Content\Art/GUI/Back", new Vector2(20, 280));
                _menu.Add(Back, b);
            }

            _menu.Setup();
        }

        protected void RequestTerrain(NetworkGamer player)
        {
            System.Threading.Thread.Sleep(20);
            PacketWriter pw = new PacketWriter();
            pw.Write(REQUESTTERRAIN);
            ts.Write(pw);
            NetworkManager.Instance.SendToTarget(player, pw);
        }

        protected void GenerateTerrain()
        {
#if WINDOWS
            GraphicsDevice gd = MiniMap.GraphicsDevice;
            int height = MiniMap.Height;
            int width = MiniMap.Width;
            MiniMap.Dispose();

            MiniMap = new Texture2D(gd, width, height);
            PContentManager.Instance.SaveContent<Texture2D>(MiniMap, "MiniMap");
#endif

            int s_hr = ts.Square_Height;
            int s_wr = ts.Square_Width;
            int t_hr = ts.Triangle_Height;
            int t_wr = ts.Triangle_Width;
            int n1_hr = ts.Noise1_Height;
            int n1_wr = ts.Noise1_Width;
            int n2_hr = ts.Noise2_Height;
            int n2_wr = ts.Noise2_Width;
            int offset = ts.Offset_From_Zero;
            float pi = 3.14159265f;

            int EdgeOffset = Terrain.EdgeOffset;

            Color[] MiniColorData = new Color[MiniMap.Height * MiniMap.Width];
            MiniMap.GetData<Color>(MiniColorData);
            for (int i = (0 + offset + EdgeOffset); i < ((MiniMap.Width * 10) + offset + EdgeOffset); i += 10)
            {
                int square_height = 75 + s_hr, square_width = 10 + s_wr;
                double square = ((square_height) * (Math.Sin((double)0.01 / square_width * 2 * pi * i))) + ((square_height / 3) * (Math.Sin((double)0.01 / square_width * 6 * pi * i))) + ((square_height / 5) * (Math.Sin((double)0.01 / square_width * 10 * pi * i))) + ((square_height / 7) * (Math.Sin((double)0.01 / square_width * 14 * pi * i))) + ((square_height / 9) * (Math.Sin((double)0.01 / square_width * 18 * pi * i)));

                int triangle_height = 100 + t_hr, triangle_width = 8 + t_wr;
                double triangle = (((triangle_height) * (Math.Sin((double)(0.01 / triangle_width) * 2 * pi * i))) - ((triangle_height / 9) * (Math.Sin((double)(0.01 / triangle_width) * 6 * pi * i))) + ((triangle_height / 25) * (Math.Sin((double)(0.01 / triangle_width) * 10 * pi * i))));

                int sine_height = -50 + n1_hr, sine_width = 40 + n1_wr;
                double sine = ((sine_height) * (Math.Sin((double)(0.01 / sine_width) * 2 * pi * i)));

                int noise_height = -25 + n2_hr, noise_width = 20 + n2_wr;
                double noise = ((noise_height) * (Math.Cos((double)(0.1 / noise_width) * 2 * pi * i)));

                double piece6 = 500;
                double temp = square + triangle + sine + noise + piece6;
                for (int j = 0; j < (MiniMap.Height * 10); j += 10)
                {
                    int index = ((i - offset - EdgeOffset) / 10) + (j * MiniMap.Width / 10);
                    if (j > temp)
                    {
                        MiniColorData[index] = Color.Black; //255 means no alpha
                    }
                    else
                    {
                        MiniColorData[index] = new Color(255, 255, 255, 255);
                    }
                }
            }
            MiniMap.SetData<Color>(MiniColorData);
            System.GC.Collect();
        }

        public override void Update(GameTime p_Time)
        {
            _menu.Update();
            _menu.ProcessInput(PlayerIndex.One); //only uncomment me if there are buttons
            pc = NetworkManager.Instance.PlayerCount;
            PlayerCount = "Players: " + pc;

            if(HasNewTS == false && ++tempcounter > 150)
            {
                NetworkManager.Instance.SendToHost("RequestTerrain");
                tempcounter = 0;
            }
        }

        public override void ProccessInput(PlayerIndex p_index)
        {
            _menu.ProcessInput(p_index);
        }

        public override void Draw(SpriteBatch p_spriteBatch, GameTime p_Time)
        {
            _menu.Draw(p_spriteBatch, p_Time);
            p_spriteBatch.DrawString(font, PlayerCount, new Vector2(30, 220), Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.MenuButton));
            if (pc > 2)
            {
                p_spriteBatch.DrawString(font, "More than 2 Players", new Vector2(20, 115), Color.Red, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.DebugLayer));
                p_spriteBatch.DrawString(font, "Are Not Supported", new Vector2(22, 145), Color.Red, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.DebugLayer));
            }
            else
            {
                p_spriteBatch.Draw(Minimap_overlay, new Vector2(20, 86), null, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.MenuButton));
                if (HasNewTS || Hosting)
                {
                    p_spriteBatch.Draw(MiniMap, new Vector2(24, 96), null, Color.White, 0.0f, Vector2.Zero, 1.0f, SpriteEffects.None, DrawLayer.GetLayer(DrawLayer.LayerDepth.BetweenMenu));
                }
            }
        }

        void WriteTerrain(NetworkGamer player)
        {
            System.Threading.Thread.Sleep(20);
            PacketWriter pw = new PacketWriter();
            pw.Write(TERRAINSAVER);
            ts.Write(pw);
            NetworkManager.Instance.SendToTarget(player, pw);
        }

        bool ReadTerrain(PacketReader pr)
        {
            HasNewTS = true;
            ts.Read(pr);
            GenerateTerrain();
            return true;
        }

        public void HostChanged(NetworkGamer oldHost, NetworkGamer newHost)
        {
            if (newHost == NetworkManager.Instance.MyNetworkGamer)
            {
                Hosting = true;
                NetworkManager.Instance.AddPlayerJoinedCallback(WriteTerrain);

                UIButton b = new UIButton(@"Content\Art/GUI/Start", new Vector2(20, 205));
                _menu.Add(Start, b);
            }
            else if (oldHost == NetworkManager.Instance.MyNetworkGamer)
            {
                UIButton b = new UIButton(@"Content\Art/GUI/Start", new Vector2(20, 205));
                _menu.Remove(Start, b);
            }
        }

        protected void Start()
        {
            if (NetworkManager.Instance.PlayerCount != 2)
            {
                return;
            }
            Game1.Instance.PopTopMenu();
            Game1.Instance.AddMenu(new Loading());

            NetworkManager nm = NetworkManager.Instance;

            int count = nm.NetworkSession.AllGamers.Count;

            {
                PacketWriter pw = new PacketWriter();
                pw.Write(TERRAINSAVER);
                ts.Write(pw);
                NetworkManager.Instance.SendToAll(pw);
            }
            NetworkManager.Instance.Update();

            teams = new Triplet<Team, PlayerIndex, TeamType>[count];

            {
                PacketWriter pw = new PacketWriter();
                pw.Write(CreatePlayers);
                pw.Write(nm.NetworkSession.AllGamers.Count);
                nm.SendToAll(pw);
            }
            NetworkManager.Instance.Update();
            {
                PacketWriter pw = new PacketWriter();
                pw.Write(SETUPFakeGamepad);
                pw.Write((int)2);
                pw.Write((int)PlayerIndex.Two);
                nm.SendToAll(pw);
                PInput.Instance.SetFakeGamePad(2, PlayerIndex.Two);
            }
            NetworkManager.Instance.Update();
            for (int i = 0; i < nm.NetworkSession.AllGamers.Count; ++i)
            {
                nm.NetworkSession.AllGamers[i].Tag = (Team)i + 1;
                {
                    PacketWriter pw = new PacketWriter();
                    pw.Write(SETUPPlayer);
                    pw.Write(i); //index == team + 1
                    pw.Write(nm.NetworkSession.AllGamers[i].Gamertag);
                    pw.Write((int)(Team)i + 1);
                    pw.Write((int)TeamType.Network);
                    pw.Write((int)PlayerIndex.Two);
                    nm.SendToAll(pw);
                }
                {
                    PacketWriter pw = new PacketWriter();
                    pw.Write(SETUPPlayer);
                    pw.Write(i); //index == team + 1
                    pw.Write(nm.NetworkSession.AllGamers[i].Gamertag);
                    pw.Write((int)(Team)(i + 1));
                    pw.Write((int)TeamType.Local);
                    pw.Write((int)PlayerIndex.One);
                    nm.SendToTarget(nm.NetworkSession.AllGamers[i], pw);
                }
                System.Threading.Thread.Sleep(5);
                NetworkManager.Instance.Update();
                if (nm.NetworkSession.AllGamers[i] == nm.MyNetworkGamer)
                {
                    teams[i] = new Triplet<Team, PlayerIndex, TeamType>((Team)i + 1, PlayerIndex.One, TeamType.Local);
                }
                else
                {
                    teams[i] = new Triplet<Team, PlayerIndex, TeamType>((Team)i + 1, PlayerIndex.Two, TeamType.Network);
                    //Could Add an AI here.. and perhaps a new player index
                }
                TeamManager.Instance.SetString(nm.NetworkSession.AllGamers[i].Gamertag, (Team)i + 1);
            }

            {
                PacketWriter pw = new PacketWriter();
                pw.Write(SETUPGame);
                nm.SendToAll(pw);
            }
            NetworkManager.Instance.RemoveCalback(NetworkManager.NETWORKSESSIONENDED);
            NetworkManager.Instance.RemoveCalback(NetworkManager.HOSTCHANGED);
            NetworkManager.Instance.Update();
            System.GC.Collect();
            NetworkManager.Instance.StartGame();
        }

        Triplet<Team, PlayerIndex, TeamType>[] teams;
        public bool CreatePlayersCallback(PacketReader pr)
        {
            Game1.Instance.PopTopMenu();
            Game1.Instance.AddMenu(new Loading());

            int total = pr.ReadInt32();
            teams = new Triplet<Team, PlayerIndex, TeamType>[total];
            return true;
        }

        public bool SetupPlayerCallback(PacketReader pr)
        {
            int index = pr.ReadInt32();
            string Name = pr.ReadString();
            Team t = (Team) pr.ReadInt32();
            TeamType type = (TeamType)pr.ReadInt32();
            PlayerIndex playerindex = (PlayerIndex)pr.ReadInt32();
            TeamManager.Instance.SetString(Name, t);
            teams[index] = new Triplet<Team, PlayerIndex, TeamType>(t, playerindex, type);
            return true;
        }

        public bool SetupFakeGamepad(PacketReader pr)
        {
            int index = pr.ReadInt32();
            PlayerIndex pi = (PlayerIndex) pr.ReadInt32();
            PInput.Instance.SetFakeGamePad(index, pi);
            return true;
        }

        public bool SetupGameCallback(PacketReader pr)
        {
            NetworkManager.Instance.RemoveAllCallbacks();
            System.GC.Collect();
         
            Game1.Instance.LoadNetworkGame(teams, ts, NetworkManager.Instance);
            return true;
        }

        public void Back(string reason)
        {
            //we could log reason..
            Back();
        }

        public override void Back()
        {
            NetworkManager.Instance.RemoveAllCallbacks();

            //NETWORKING
            if(Hosting)
            {
                Game1.Instance.PopTopMenu();
                NetworkManager.Instance.Disconnect();
                System.Threading.Thread.Sleep(10);//hopefully this gives somecleanup time
                Game1.Instance.AddMenu(new HostNetworkMenu());
            }
            else
            {
                Game1.Instance.PopTopMenu();
                NetworkManager.Instance.Disconnect();
                System.Threading.Thread.Sleep(10);//hopefully this gives somecleanup time
                Game1.Instance.AddMenu(new JoinNetworkMenu());
            }
        }
    }
}
